﻿using HybridCLR;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using UnityEngine;
using UnityEngine.Rendering.Universal;
using ZyGame.Execute;

namespace ZyGame.Game
{
    class CameraSortData
    {
        public int index;
        public Camera camera;
    }

    public enum CameraSort
    {
        Background,
        GameWorld,
        OhterCamera1,
        OhterCamera2,
        OhterCamera3,
        OhterCamera4,
        OhterCamera5,
        UICamera = 999,
    }

    internal class WorldManager : Singleton<WorldManager>
    {
        private World current;
        private Type lastWorldType;
        private World backupWorld;
        private Camera mainCamera;
        internal UniversalAdditionalCameraData UniversalData;
        private List<CameraSortData> sortDatas = new List<CameraSortData>();
        private Dictionary<string, World> worlds = new Dictionary<string, World>();


        public void Initialized()
        {
            this.mainCamera = Camera.main;
            if (this.mainCamera is null)
            {
                this.mainCamera = new GameObject("Main Camera").AddComponent<Camera>();
                this.mainCamera.tag = "MainCamera";
            }

            UniversalData = this.mainCamera.GetUniversalAdditionalCameraData();
            UniversalData.renderType = CameraRenderType.Base;
            GameObject.DontDestroyOnLoad(this.mainCamera);
            Client.Tools.AddUpdate(OnUpdateWorld);
            Client.Tools.LateUpdate(OnLateUpdate);
            Client.Tools.AddFixedUpdate(OnFixedUpdate);
        }

        public void EntrySubGame(int id, string entryName, params object[] paramsList)
        {
            Client.Window.Wait("Please Waiting...");
            string className = entryName.Substring(0, entryName.LastIndexOf('.'));
            string methodName = entryName.Substring(entryName.LastIndexOf('.') + 1);
            Type t = AppDomain.CurrentDomain.GetTypeWithFullName(className);
            object classObject = Activator.CreateInstance(t);
            MethodInfo methodInfo = t.GetMethod(methodName, BindingFlags.Static | BindingFlags.Public);
            methodInfo.Invoke(null, new[] { paramsList });
            current.Clear();
            mainCamera.gameObject.SetActive(false);
            Client.Window.ClearView();
        }

        public void ExitSubGame()
        {
            mainCamera.gameObject.SetActive(true);
            current.Reload();
        }

        public void AddCamera(int sort, Camera camera)
        {
            if (camera is null)
            {
                Client.Console.WriteLine("not camera");
                return;
            }

            for (int i = 0; i < sortDatas.Count; i++)
            {
                if (sortDatas[i].index == sort)
                {
                    if (sortDatas[i].camera is null)
                    {
                        continue;
                    }

                    UniversalData.cameraStack.Remove(sortDatas[i].camera);
                    GameObject.DestroyImmediate(sortDatas[i].camera.gameObject);
                }
            }

            camera.GetUniversalAdditionalCameraData().renderType = CameraRenderType.Overlay;
            sortDatas.Add(new CameraSortData() { index = sort, camera = camera });
            sortDatas.Sort((a, b) => a.index > b.index ? 1 : -1);
            UniversalData.cameraStack.Clear();
            sortDatas.ForEach(x => UniversalData.cameraStack.Add(x.camera));
            AudioListener listener = camera.GetComponent<AudioListener>();
            if (listener is not null)
            {
                GameObject.DestroyImmediate(listener);
            }
        }

        public void RemoveCamera(Camera camera)
        {
            UniversalData.cameraStack.Remove(camera);
            CameraSortData temp = sortDatas.Find(x => x.camera == camera);
            if (temp is not null)
            {
                sortDatas.Remove(temp);
            }

            GameObject.DestroyImmediate(camera.gameObject);
        }

        private void OnFixedUpdate()
        {
            if (current == null)
            {
                return;
            }

            try
            {
                current.FixedUpdate();
            }
            catch (Exception e)
            {
                Client.Console.WriteError(e);
            }
        }

        private void OnUpdateWorld()
        {
            if (current == null)
            {
                return;
            }

            try
            {
                current.Update();
            }
            catch (Exception e)
            {
                Client.Console.WriteError(e);
            }
        }

        private void OnLateUpdate()
        {
            if (current == null)
            {
                return;
            }

            try
            {
                current.LateUpdate();
            }
            catch (Exception e)
            {
                Client.Console.WriteError(e);
            }
        }

        public World CreateWorld(Type worldType)
        {
            string name = worldType.Name;
            World world = GetWorld(name);
            if (world is null)
            {
                world = (World)Activator.CreateInstance(worldType);
                world.Awake();
                worlds.Add(name, world);
            }

            current = world;
            return world;
        }

        public World GetWorld(string name)
        {
            if (worlds.TryGetValue(name, out World world))
            {
                return world;
            }

            return default;
        }

        public T GetWorld<T>() where T : World, new()
        {
            if (worlds.TryGetValue(typeof(T).Name, out World world))
            {
                return (T)world;
            }

            return default;
        }

        public void CloseWorld(string name)
        {
            if (!worlds.TryGetValue(name, out World world))
            {
                return;
            }

            worlds.Remove(name);
            world.Dispose();
            if (current == world)
            {
                current = null;
            }
        }
    }
}